package by.msokoloff.javaweb2015.dataaccess.impl;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.springframework.stereotype.Repository;

import by.msokoloff.javaweb2015.dataaccess.FlightDao;
import by.msokoloff.javaweb2015.datamodel.Airport;
import by.msokoloff.javaweb2015.datamodel.Flight;
import by.msokoloff.javaweb2015.datamodel.Flight_;
import by.msokoloff.javaweb2015.datamodel.enums.FlightStatus;

@Repository
public class FlightDaoImpl extends AbstractDaoImpl<Flight> implements FlightDao {

	protected FlightDaoImpl() {
		super(Flight.class);
	}

	@Override
	public List<Flight> getDirectFlights(Airport depAirport, Airport dstAirport, Date date) {

		return getDirectFlights(depAirport, dstAirport, date, date);

	}

	@Override
	public List<Flight> getDirectFlights(Airport depAirport, Airport dstAirport, Date fromDate, Date toDate) {

		Calendar calendar = new GregorianCalendar();
		calendar.setTime(fromDate);
		calendar.set(Calendar.MILLISECOND, 0);
		calendar.set(Calendar.SECOND, 0);
		calendar.set(Calendar.MINUTE, 0);
		calendar.set(Calendar.HOUR_OF_DAY, 0);
		Date d0 = calendar.getTime();
		calendar.setTime(toDate);
		calendar.set(Calendar.MILLISECOND, 0);
		calendar.set(Calendar.SECOND, 0);
		calendar.set(Calendar.MINUTE, 0);
		calendar.set(Calendar.HOUR_OF_DAY, 0);
		calendar.add(Calendar.SECOND, 60 * 60 * 24 - 1);
		Date d1 = calendar.getTime();

		CriteriaBuilder cb = getEm().getCriteriaBuilder();
		CriteriaQuery<Flight> cq = cb.createQuery(Flight.class);
		Root<Flight> root = cq.from(Flight.class);
		Predicate p1 = cb.equal(root.get(Flight_.status), FlightStatus.PLANNED);
		Predicate p2 = cb.equal(root.get(Flight_.departureAirport), depAirport);
		Predicate p3 = cb.equal(root.get(Flight_.destinationAirport), dstAirport);
		Predicate p4 = cb.between(root.get(Flight_.departureDate), d0, d1);
		cq.where(cb.and(p1, p2, p3, p4));

		return getEm().createQuery(cq).getResultList();

	}

	@Override
	public List<List<Flight>> getInDirectFlights(Airport depAirport, Airport dstAirport, Date date) {

		return getInDirectFlights(depAirport, dstAirport, date, date);

	}

	@Override
	public List<List<Flight>> getInDirectFlights(Airport depAirport, Airport dstAirport, Date fromDate, Date toDate) {

		List<List<Flight>> results = new ArrayList<List<Flight>>();

		Calendar calendar = new GregorianCalendar();
		calendar.setTime(fromDate);
		calendar.set(Calendar.MILLISECOND, 0);
		calendar.set(Calendar.SECOND, 0);
		calendar.set(Calendar.MINUTE, 0);
		calendar.set(Calendar.HOUR_OF_DAY, 0);
		Date d0 = calendar.getTime();
		calendar.setTime(toDate);
		calendar.set(Calendar.MILLISECOND, 0);
		calendar.set(Calendar.SECOND, 0);
		calendar.set(Calendar.MINUTE, 0);
		calendar.set(Calendar.HOUR_OF_DAY, 0);
		calendar.add(Calendar.SECOND, 60 * 60 * 24 - 1);
		Date d1 = calendar.getTime();

		CriteriaBuilder cb = getEm().getCriteriaBuilder();
		CriteriaQuery<Flight> cq = cb.createQuery(Flight.class);
		Root<Flight> root = cq.from(Flight.class);
		Predicate p1 = cb.equal(root.get(Flight_.status), FlightStatus.PLANNED);
		Predicate p2 = cb.equal(root.get(Flight_.departureAirport), depAirport);
		Predicate p3 = cb.notEqual(root.get(Flight_.destinationAirport), dstAirport);
		Predicate p4 = cb.between(root.get(Flight_.departureDate), d0, d1);
		cq.where(cb.and(p1, p2, p3, p4));
		List<Flight> firstHop = getEm().createQuery(cq).getResultList();

		for (Flight f1 : firstHop) {

			calendar = new GregorianCalendar();
			calendar.setTime(f1.getDepartureDate());
			calendar.add(Calendar.MINUTE, f1.getDuration() + 30);
			d0 = calendar.getTime();
			calendar.add(Calendar.MINUTE, 180);
			d1 = calendar.getTime();

			cb = getEm().getCriteriaBuilder();
			cq = cb.createQuery(Flight.class);
			root = cq.from(Flight.class);
			p1 = cb.equal(root.get(Flight_.status), FlightStatus.PLANNED);
			p2 = cb.equal(root.get(Flight_.departureAirport), f1.getDestinationAirport());
			p3 = cb.equal(root.get(Flight_.destinationAirport), dstAirport);
			p4 = cb.between(root.get(Flight_.departureDate), d0, d1);
			cq.where(cb.and(p1, p2, p3, p4));
			List<Flight> secondHop = getEm().createQuery(cq).getResultList();

			if (secondHop.size() == 0) {
				continue;
			}

			for (Flight f2 : secondHop) {
				List<Flight> flightPair = new ArrayList<Flight>();
				flightPair.add(f1);
				flightPair.add(f2);
				results.add(flightPair);
			}

		}

		return results;
	}

}